import { message } from "ant-design-vue";
import { transferDicts } from "../dicts/scoreToneTransfer";

// 音阶比值
const typeRate = {
  "whole": 1,
  "half": 1 / 2,
  "quarter": 1 / 4,
  "eighth": 1 / 8,
  "16th": 1 / 16,
  "32nd": 1 / 32,
}

// note方法
export const noteUtils = {
  isChord: (note) => {
    return note?.getElementsByTagName?.("chord")?.length > 0;
  },
  isRest: (note) => {
    return note?.getElementsByTagName?.("rest")?.length > 0;
  },
  isTiedStart: (note) => {
    const tied = note?.getElementsByTagName?.("tied")?.[0];
    return tied?.getAttribute?.("type") === 'start';
  },
  isTiedStop: (note) => {
    const tied = note?.getElementsByTagName?.("tied")?.[0];
    return tied?.getAttribute?.("type") === 'stop';
  },
  isGrace: (note) => {
    return note?.getElementsByTagName?.("grace")?.length > 0;
  }
}

// 状态池
const state = {
  // 重唱次数，第一次唱时是1
  nowCount: 1,
  // 重唱标记点
  repeatDot: [] as any[],
  // endingNumber
  endingNumber: "0",
  endingState: '',
}

// measure子项基类
class measureItemBase {
  model: any;
  constructor(model) {
    this.model = model;
  }
  // 处理数据(默认不处理)
  dealData(measuresData, model) { }
  async next(callback) {
    return new Promise((resolve) => {
      const measuresLen = this.model.measures.length;
      const nowMeasure = this.model.measures[this.model.measureIdx];
      const itemLen = nowMeasure.childNodes.length;
      if (this.model.itemIdx < itemLen - 1) {
        this.model.itemIdx++;
      } else {
        this.model.measureIdx++;
        this.model.itemIdx = 0;
        // 当前索引
        this.model.measureNowIdx++;
      }
      setTimeout(async () => {
        if (this.model.measureIdx <= measuresLen - 1) {
          await callback(this.model.measureIdx, this.model.itemIdx, this.model.measureNowIdx);
        }
        resolve(true);
      }, 10)
    })
  }
}
// measure子项--barline
class measureItemBarline extends measureItemBase {
  dealData(measuresData, model) {
    const childs = model?.measures?.[model.measureIdx]?.childNodes;
    const barline = childs?.[model.itemIdx];
    // 重唱标签
    const repeat = barline?.getElementsByTagName?.("repeat")?.[0];
    if (repeat) {
      // 重唱开始标签
      if (repeat.getAttribute?.("direction") === "forward") {
        state.repeatDot.push({
          measureIdx: model.measureIdx,
          itemIdx: model.itemIdx,
        })
      }
    }
    // 分段ending
    const ending = barline?.getElementsByTagName?.("ending")?.[0];
    const endingNumber = ending?.getAttribute?.("number");
    const endingState = ending?.getAttribute?.("type");
    state.endingNumber = endingNumber || "0";
    state.endingState = endingState || "";
    if (Number(endingNumber) > 1 && endingState === "start") {
      state.nowCount = Number(endingNumber);
    }
    if (Number(endingNumber) > 1 && (endingState === "stop" || endingState === "discontinue")) {
      state.nowCount = 1;
    }
  }
  async next(callback) {
    return new Promise(async (resolve) => {
      const childs = this.model?.measures?.[this.model.measureIdx]?.childNodes;
      const barline = childs?.[this.model.itemIdx];
      // 重唱标签
      const repeat = barline?.getElementsByTagName?.("repeat")?.[0];
      if (repeat?.getAttribute?.("direction") === "backward") {
        state.nowCount = 1;
      }
      if (state.repeatDot.length > 0 && repeat?.getAttribute?.("direction") === "backward") {
        const repeatMeasureIdx = state.repeatDot[0].measureIdx;
        const repeatItemIdx = state.repeatDot[0].itemIdx;
        this.model.measureIdx = repeatMeasureIdx;
        this.model.itemIdx = repeatItemIdx + 1;
        state.nowCount++;
        state.repeatDot = [];
        this.model.measureNowIdx++;
        await callback(this.model.measureIdx, this.model.itemIdx, this.model.measureNowIdx);
        resolve(true);
        return;
      }
      await super.next(callback);
      resolve(true);
    })
  }
}
// measure子项--node
class measureItemNode extends measureItemBase {
  dealData(measuresData, model) {
    // 分段ending不符合时不执行操作
    if (state.endingState === 'start' && state.endingNumber !== state.nowCount.toString()) {
      return;
    }
    const childs = model?.measures?.[model.measureIdx]?.childNodes;
    const note = childs?.[model.itemIdx];
    const staff = note?.getElementsByTagName?.("staff")?.[0]?.textContent;
    let bol = false;
    if (model.hasP2) {
      bol = staff === undefined;
    } else {
      if (model.hasStaff1) {
        bol = staff === "1";
      } else {
        bol = staff === undefined;
      }
    }
    if (bol) {
      let lyric: any = {};
      const lyricDom = note?.getElementsByTagName?.("lyric");
      let lyrics = Array.prototype.filter.call(lyricDom, x => x?.getAttribute?.("number") === state.nowCount.toString());
      let lyrics2 = Array.prototype.filter.call(lyricDom, x => x?.getAttribute?.("number") === "1");
      let tLyrics = lyrics.length > 0 ? lyrics : lyrics2;
      lyric.word = tLyrics?.[0]?.getElementsByTagName?.("text")?.[0]?.textContent || "";
      lyric.type = note?.getElementsByTagName?.("type")?.[0]?.textContent || "";
      lyric.duration = note?.getElementsByTagName?.("duration")?.[0]?.textContent || "";
      lyric.measureIdx = model.measureNowIdx;
      lyric.isRest = noteUtils.isRest(note);
      lyric.isChord = noteUtils.isChord(note);
      lyric.isGrace = noteUtils.isGrace(note);
      lyric.isTiedStart = noteUtils.isTiedStart(note);
      lyric.isTiedStop = noteUtils.isTiedStop(note);
      measuresData.push(lyric);
    }
  }
}
// measure子项工厂类
class MeasureItemFactory {
  createMeasureItem(type, model) {
    switch (type) {
      case "note":
        return new measureItemNode(model);
      case "barline":
        return new measureItemBarline(model);
      default:
        return new measureItemBase(model);
    }
  }
}

class MeasuresClass {
  measures;
  measuresData = [];
  measureNowIdx = 0;
  measureIdx = 0;
  itemIdx = 0;
  hasP2 = false;
  hasStaff1 = false;
  hasBackwardWithoutForward = false;
  constructor(obj) {
    this.hasP2 = obj.hasP2;
    this.hasStaff1 = obj.hasStaff1;
    this.hasBackwardWithoutForward = obj.hasBackwardWithoutForward;
    if (this.hasBackwardWithoutForward) {
      state.repeatDot.push({
        measureIdx: 0,
        itemIdx:0,
      })
    }
  }
  // 获取小节
  getMeasures(part) {
    return part.getElementsByTagName("measure");
  }
  // 填入小节
  setMeasures(measures) {
    this.measures = measures;
  }
  // 遍历
  async traverse() {
    await this.dealNowMeasureItem();
  }
  // 处理当前索引measure子项
  async dealNowMeasureItem(measureIdx = undefined, itemIdx = undefined, measureNowIdx = undefined) {
    measureIdx !== undefined && (this.measureIdx = measureIdx);
    itemIdx !== undefined && (this.itemIdx = itemIdx);
    measureNowIdx !== undefined && (this.measureNowIdx = measureNowIdx);
    //获取当前索引measure
    const nowMeasure = this.measures[this.measureIdx];
    //获取当前索引measure子项
    const nowItem = nowMeasure.childNodes[this.itemIdx];
    // 当前子项标签名
    const nowItemName = nowItem.nodeName;
    // 当前子项类
    const measureItemFactory = new MeasureItemFactory();
    const measureItem = measureItemFactory.createMeasureItem(nowItemName, {
      measures: this.measures,
      measureIdx: this.measureIdx,
      itemIdx: this.itemIdx,
      measureNowIdx: this.measureNowIdx,
      hasP2: this.hasP2,
      hasStaff1: this.hasStaff1,
    });
    // 执行当前子项数据操作
    measureItem?.dealData?.(this.measuresData, {
      measures: this.measures,
      measureIdx: this.measureIdx,
      itemIdx: this.itemIdx,
      measureNowIdx: this.measureNowIdx,
      hasP2: this.hasP2,
      hasStaff1: this.hasStaff1,
    });
    // 执行最后一步，next方法
    await measureItem.next(this.dealNowMeasureItem.bind(this));
  }
  // 获取数据
  getData() {
    let arr: any[] = [];
    for (let i = 0; i <= this.measureNowIdx; i++) {
      let arr2: any[] = [];
      this.measuresData.forEach((x: any) => {
        if (x.measureIdx === i) {
          arr2.push(x);
        }
      })
      if (arr2.length > 0) {
        arr.push(arr2);
      }
    }
    return arr;
  }
}
export default class museXmlDealClass {
  part;
  hasP2;
  hasStaff1;
  // 有重唱结束标识没有开始标识，即从曲首开始
  hasBackwardWithoutForward = false;
  constructor(xml) {
    this.part = xml.getElementsByTagName("part")[0];
    this.hasP2 = xml.getElementsByTagName("part").length > 1;
    // 判断是否有重唱结束标识
    const hasBackward = this.getHasBackword(this.part);
    // 判断是否有重唱开始标识
    const hasForward = this.getHasForward(this.part);
    (hasBackward && !hasForward) && (this.hasBackwardWithoutForward = true);
    this.hasStaff1 = (() => {
      let bol = false;
      const part = xml.getElementsByTagName("part")[0];
      const staffs = part?.getElementsByTagName?.("staff") || [];
      Array.prototype.forEach.call(staffs, x => {
        if (x?.textContent === '1') {
          bol = true;
        }
      })
      return bol;
    })()
  }
  async deal() {
    const measureInstance = new MeasuresClass({ hasP2: this.hasP2, hasStaff1: this.hasStaff1, hasBackwardWithoutForward: this.hasBackwardWithoutForward });
    // 获取所有小节
    const measures = await measureInstance.getMeasures(this.part);
    // 填入所有小节
    await measureInstance.setMeasures(measures)
    const hide = message.loading("xml文件解析中...", 0);
    // 遍历小节内容
    await measureInstance.traverse();
    const measuresData = measureInstance.getData();
    // 曲谱调号
    const scoreToneData = await this.getScoreTone();
    hide();
    return {
      measures: measuresData,
      score_tone: scoreToneData,
    }
  }
  // 获取曲谱调号
  async getScoreTone() {
    const measure_first = this.part.getElementsByTagName("measure")[0];
    const attributes = measure_first.getElementsByTagName("attributes")[0];
    const key = attributes.getElementsByTagName("key")[0];
    const fifths = key.getElementsByTagName("fifths")[0];
    return transferDicts[fifths.textContent.toString()];
  }
  // 获取是否有重唱结束标识
  getHasBackword(part) {
    const barline = part.getElementsByTagName("barline");
    return Array.prototype.some.call(barline, x => {
      const repeat = x.getElementsByTagName("repeat")[0];
      return repeat?.getAttribute?.("direction") === "backward";
    })
  }
  // 获取是否有重唱开始标识
  getHasForward(part) {
    const barline = part.getElementsByTagName("barline");
    return Array.prototype.some.call(barline, x => {
      const repeat = x.getElementsByTagName("repeat")[0];
      return repeat?.getAttribute?.("direction") === "forward";
    })
  }
}

export const getMultiple = (measure) => {
  const durations = measure.reduce((prev, cur, index, arr) => {
    return prev + Number(cur?.duration || 0);
  }, 0)
  return Number((1920 / durations).toFixed(0));
}